#include "temperature.h"
#include <QString>
#include <fcntl.h>
#include <unistd.h>
#ifndef __WIN32__
    #include <sys/mman.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <iostream>
#include <QDir>
#include <QFileInfo>
#include <QtDebug>
#include "cmdsingle.h"
using namespace std;

#define CPU_SENSOR_BASE_ADDRESS    0x28100D00
#define CPU_SENSOR_CTLR            (0)
#define CPU_SENSOR01_VAL           (0x4)
#define CPU_SENSOR23_VAL           (0x8)
#define CPU_SENSOR45_VAL           (0xC)
#define CPU_SENSOR67_VAL           (0x10)
#define MAP_SIZE        0x20

/* Convert table of sensor value to temperature */
static int cpu_val2temp[35][2] = {
    {3795, -40000},
    {3787, -35000},
    {3779, -30000},
    {3769, -25000},
    {3761, -20000},
    {3751, -15000},
    {3743, -10000},
    {3733, -5000},
    {3723, 0},
    {3713, 5000},
    {3703, 10000},
    {3693, 15000},
    {3683, 20000},
    {3673, 25000},
    {3663, 30000},
    {3651, 35000},
    {3641, 40000},
    {3629, 45000},
    {3617, 50000},
    {3605, 55000},
    {3593, 60000},
    {3581, 65000},
    {3569, 70000},
    {3555, 75000},
    {3543, 80000},
    {3529, 85000},
    {3515, 90000},
    {3501, 95000},
    {3487, 100000},
    {3471, 105000},
    {3457, 110000},
    {3441, 115000},
    {3425, 120000},
    {3409, 125000},
    {0,    -1},
};

#define FT2004_TEMP_DIR     "/sys/devices/platform/FTSC0001:00/FTSS0001:00"
#define FT2004_TEMP_SENSOR0 "/sys/devices/platform/FTSC0001:00/FTSS0001:00/hwmon/hwmon0/temp1_input"
#define FT2004_TEMP_SENSOR1 "/sys/devices/platform/FTSC0001:00/FTSS0001:00/hwmon/hwmon1/temp1_input"

static QString ft2004_temp_sensor = FT2004_TEMP_SENSOR1;
Temperature::Temperature()
{
    memDev_fd   = 0;
    map_base    = NULL;
    data_base   = NULL;
    map_len     = MAP_SIZE;

    QDir tempDir(FT2004_TEMP_DIR);
    if(tempDir.exists()){
        cpu_type = FT2004;
        // 查找温感位置
        QFile file(FT2004_TEMP_SENSOR0);
        if(file.open(QIODevice::ReadOnly)){
            ft2004_temp_sensor = FT2004_TEMP_SENSOR0;
        }
        file.close();

        qDebug() << "当前CPU型号是ft2004, 温感：" << ft2004_temp_sensor;
        return;
    }else{
        qDebug() << "当前CPU型号是ft1500a.\n";
        cpu_type = FT1500A;
    }

#ifndef __WIN32__
#ifdef CPU_HAS_SENSOR
    memDev_fd = ::open("/dev/mem", O_RDWR | O_NDELAY);
    if(memDev_fd < 0){
        cout << "open(/dev/mem) failed.";
        return;
    }
    off_t pageSize = getpagesize();
    off_t actualOffset = CPU_SENSOR_BASE_ADDRESS - CPU_SENSOR_BASE_ADDRESS % pageSize;  //保持map页对齐
    map_len            = MAP_SIZE + CPU_SENSOR_BASE_ADDRESS % pageSize;
    map_base = (unsigned char * )mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_SHARED, memDev_fd, actualOffset);
    if(map_base != NULL){
        data_base = map_base + CPU_SENSOR_BASE_ADDRESS % pageSize;
        //out << "map_base  : " << QString::number(map_base , 16) << endl;
        //cout << "data_base : " << QString::number(data_base, 16) << endl;
        printf("map_base: %lx\n", (unsigned long)map_base);
        printf("data_base: %lx\n", (unsigned long)data_base);
        SwitchSensor(true);
    }
#endif
#endif
}

Temperature::~Temperature()
{
    if(memDev_fd > 0){
        ::close(memDev_fd);
    }
#ifndef __WIN32__
    if(map_base != NULL){
        munmap(map_base, map_len);
    }
#endif
}

void Temperature::SwitchSensor(bool on)
{
    quint32 Sensor_Ctl;

    if(map_base != NULL){
        Sensor_Ctl = mmioRead32(CPU_SENSOR_CTLR);
        if(on){
            mmioWrite32(CPU_SENSOR_CTLR, Sensor_Ctl & ~0x80000000);
        }else{
            mmioWrite32(CPU_SENSOR_CTLR, Sensor_Ctl | 0x80000000);
        }
    }
}

/* 获取显卡温度
 * ret: 为温度实际值*1000
 * */
int Temperature::GetCpuTemperature()
{
    if(cpu_type == FT2004){
        int temp = 0;
        QFile file(ft2004_temp_sensor);
        if(file.open(QIODevice::ReadOnly)){
            temp = QString(file.readLine(16)).toInt();
            if(temp < 0 || temp > 200*1000)
                temp = 0;
        }
        file.close();
        return temp;
    }

    if(map_base == NULL){
        return 0;
    }
    quint32 Sensor01_Val, Sensor23_Val, Sensor45_Val, Sensor67_Val;
    Sensor01_Val = mmioRead32(CPU_SENSOR01_VAL);
    Sensor23_Val = mmioRead32(CPU_SENSOR23_VAL);
    Sensor45_Val = mmioRead32(CPU_SENSOR45_VAL);
    Sensor67_Val = mmioRead32(CPU_SENSOR67_VAL);

    long  TempVal = 0;
    TempVal += cpu_calc_temprature((Sensor01_Val) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor01_Val >> 16) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor23_Val) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor23_Val >> 16) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor45_Val) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor45_Val >> 16) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor67_Val) & 0xFFF);
    TempVal += cpu_calc_temprature((Sensor67_Val >> 16) & 0xFFF);

    int temp = static_cast<qint32>(TempVal / 8);
    int TempValInCelsius =  temp > 0 ? temp : 0;
    return TempValInCelsius;
}

/* 获取显卡温度
 * ret: 为温度实际值
 * */
float Temperature::GetVideoTemperature()
{
    QString vtemp = CmdSingle::StartOnecmd("sensors | grep 'temp1' | awk '{print $2}'", 1000);
    if(vtemp.length() > 2){
        vtemp.chop(3);
        return (vtemp.toFloat());
    }
    return 0;
}

/* 获取硬盘温度
 * ret: 为温度实际值
 * */
float Temperature::GetHddTemperature()
{
    QString vtemp = CmdSingle::StartOnecmd("hddtemp /dev/sda", 1000);
    if(vtemp.length() > 2){
        vtemp.chop(3);
        return (vtemp.right(3).toFloat());
    }
    return 0;
}

quint32 Temperature::mmioRead32(int offsetAddr)
{
    return *(volatile unsigned int *)(data_base+offsetAddr);
}

void Temperature::mmioWrite32(int offsetAddr, quint32 value)
{
    *(volatile unsigned int *)(data_base+offsetAddr) = value;
}

int Temperature::cpu_calc_temprature(int sensor_val)
{
    int index = 0;

    while (cpu_val2temp[index + 1][0] > sensor_val)
       index ++;

    return cpu_val2temp[index][1];
}
